package com.ujoku.interceptor.admin.oa;

import com.labillusion.core.crypto.HMAC;
import com.labillusion.core.platform.exception.UserAuthorizationException;
import com.labillusion.core.platform.web.filter.RequestWrapper;
import com.labillusion.core.util.Convert;
import com.labillusion.core.util.EncodingUtils;
import com.labillusion.core.util.StringUtils;
import com.ujoku.domain.Client;
import com.ujoku.interceptor.AuthConstant;
import com.ujoku.service.ClientService;
import com.ujoku.service.admin.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by Administrator on 14-10-18.
 */
public class OAClientAuthInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    private ClientService clientService;

    @Autowired
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        authenticateClient((RequestWrapper) request);

        return true;
    }

    private void authenticateClient(RequestWrapper request) throws Exception {

        String clientId = request.getHeader(AuthConstant.HEADER_CLIENT_ID);
        if (!StringUtils.hasText(clientId))
            throw new UserAuthorizationException("client-id header is required");

        String ascription = URLDecoder.decode(request.getHeader(AuthConstant.HEADER_ASCRIPTION), "utf-8");
        if (!StringUtils.hasText(ascription))
            throw new UserAuthorizationException("ascription header is required");

        String signature = request.getHeader(AuthConstant.HEADER_Client_Signature);
        if (!StringUtils.hasText(signature))
            throw new UserAuthorizationException("client-signature header is required");

        Map<String,Object> query = new HashMap<>();
        query.put("ClientId",clientId);
        Client client = (Client) clientService.selectOne(query);

        if(client == null)
            throw new UserAuthorizationException("client-id is invalid");

        isExpired(request);

        String message = buildSignedMessage(request);
        String exceptedSignature =  EncodingUtils.encryptBASE64(HMAC.encryptHMAC(message.getBytes(Charset.forName("UTF-8")), client.getSecretKey()));
        //验证签名
        if(!signature.equalsIgnoreCase(exceptedSignature))
            throw new UserAuthorizationException("client-signature header is invalid");

        request.setAttribute("client", client);
        request.getSession().setAttribute("ascription", ascription);

    }

    /**
     * 原始签名
     *
     * @param request
     * @return
     */
    private String buildSignedMessage(RequestWrapper request) throws UnsupportedEncodingException {
        String timestamp = request.getHeader(AuthConstant.HEADER_Timestamp);
        String clientId = request.getHeader(AuthConstant.HEADER_CLIENT_ID);
        String ascription = URLDecoder.decode(request.getHeader(AuthConstant.HEADER_ASCRIPTION), "utf-8");

        StringBuilder builder = new StringBuilder(300);
        builder.append("uri=").append(request.getRequestURI());
        builder.append("&clientId=").append(clientId);
        builder.append("&method=").append(request.getMethod().toUpperCase());
        builder.append("&ascription=").append(ascription);
        builder.append("&timestamp=").append(timestamp);

        return builder.toString();
    }

    private void isExpired(RequestWrapper request) {
        String timestamp = request.getHeader(AuthConstant.HEADER_Timestamp);

        if (!StringUtils.hasText(timestamp))
            throw new UserAuthorizationException("timestamp required");

        Long clientTimestamp = Convert.toLong(timestamp, null);
        if (clientTimestamp == null) {
            throw new UserAuthorizationException("timestamp invalid");
        }
        Long serverTimeStamp = System.currentTimeMillis();
        if (clientTimestamp + (5 * 60 * 1000) < serverTimeStamp) {
            throw new UserAuthorizationException(String.format("request expired, clientTime=%s, serverTime=%s", clientTimestamp, serverTimeStamp));
        }
    }


}
